package org.example.business_logic.action;

import javafx.util.Pair;
import lombok.AllArgsConstructor;
import org.example.business_logic.component.Ball;
import org.example.physics_engine.collision.manifold.Penetration;
import org.example.physics_engine.collision.CollisionFilter;
import org.example.physics_engine.collision.detector.BasicCollisionDetector;
import org.example.physics_engine.collision.detector.DetectorResult;
import org.example.physics_engine.collision.detector.DetectorUtil;
import org.example.physics_engine.collision.manifold.Manifold;
import org.example.physics_engine.collision.manifold.ManifoldSolver;
import org.example.physics_engine.body.PhysicsBody;

import java.util.ArrayList;
import java.util.List;

@AllArgsConstructor
public class BallAction implements UpdateListener {
    //可能不止一个ball，是一个实体
    private final List<PhysicsBody> balls;

    /**
     * 在new collisiondetector的同时重写父类中的detect，调用processDetect
     */
    private final BasicCollisionDetector basicCollisionDetector = new BasicCollisionDetector() {
        @Override
        public List<Pair<Manifold, Pair<PhysicsBody, PhysicsBody>>> detect(List<PhysicsBody> bodies1, List<PhysicsBody> bodies2, List<CollisionFilter> listeners) {
            List<Pair<Manifold, Pair<PhysicsBody, PhysicsBody>>> manifolds = new ArrayList<>();
            ManifoldSolver manifoldSolver = new ManifoldSolver();//接触流形求解器，把penetration转化为manifold
            for (int i = 0; i < bodies1.size() - 1; i++) {
                for (int j = i + 1; j < bodies1.size(); j++) {
                    PhysicsBody physicsBody1 = bodies1.get(i);
                    PhysicsBody physicsBody2 = bodies1.get(j);
                    Ball ball1 = (Ball) physicsBody1.getShape();
                    Ball ball2 = (Ball) physicsBody2.getShape();
                    Manifold manifold = this.processDetect(manifoldSolver, ball1, ball2);
                    if (manifold != null) {
                        Pair<PhysicsBody, PhysicsBody> physicsBodyPhysicsBodyPair = new Pair<>(physicsBody1, physicsBody2);
                        manifolds.add(new Pair<>(manifold, physicsBodyPhysicsBodyPair));
                    }
                }
            }
            return manifolds;
        }

        /**
         * 把处理过程封装到processDetect中
         * 虽然basicCollisionDetector中就有processDetect，但是那个针对所有body，这里的专门用来处理Ball之间的,函数名一样没事吗？
         * 没事，是在重写
         * @param manifoldSolver
         * @param ball1
         * @param ball2
         * @return
         */
        private Manifold processDetect(ManifoldSolver manifoldSolver, Ball ball1, Ball ball2) {
            if (!DetectorUtil.AABBDetect(ball1, ball2)) {
                return null;
            }

            Penetration penetration = new Penetration();
            DetectorResult detect = DetectorUtil.circleDetect(ball1, ball2, null, penetration);
            //判断是否碰撞
            if (!detect.isHasCollision()) {
                return null;
            }
            Manifold manifold = new Manifold();
            //得到流形
            if (!manifoldSolver.getManifold(penetration, ball1, ball2, detect.getApproximateShape(), manifold)) {
                return null;
            }
            return manifold;
        }
    };

    /**
     * 每个时钟信号检查一次有没有两个小球发生碰撞，如果有记录下<碰撞点组成的流形,那两个小球>
     * @return list
     */
    @Override
    public List<Pair<Manifold, Pair<PhysicsBody, PhysicsBody>>> tick(){
        return basicCollisionDetector.detect(balls,null,null);
    }
}
